package com.lvy.hczn.front.business.config.socket;


import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.lvy.hczn.front.business.common.CacheConstants;
import com.lvy.hczn.front.business.common.Constants;
import com.lvy.hczn.front.business.dto.MessageInfo;
import com.lvy.hczn.front.common.core.redis.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * 客户端和服务端都是通过事件来交互的
 * 用于监听客户端websocket的事件
 * 同时也可以往客户端发送事件(客户端自己可以监听)
 */
@Component
@Slf4j
public class SocketIoHandler {

    @Autowired
    private SocketIOServer socketIoServer;

    @Autowired
    private RedisCache redisCache;

    /**
     * 线程安全的map,用于保存和客户端的回话
     * <p>
     * 如果是使用集群部署的情况下则不能这么使用,
     * 因为客户端每次命中的服务不一定是上次命中那个
     * 集群解决方案:使用redis的发布订阅或者消息中间件的发布订阅
     * 这样,每个服务都有listener监听着,然后可以拿到对应的客户端socketclient
     */
    public static ConcurrentMap<String, SocketIOClient> socketIOClientMap = new ConcurrentHashMap<>();

    /**
     * 客户端连接的时候触发
     *
     * @param client
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        //
        String clientId = client.getHandshakeData().getSingleUrlParam("clientId");
        if(StrUtil.isEmpty(clientId)){
            MessageInfo messageInfo = new MessageInfo();
            messageInfo.setCode(Constants.FAIL);
            messageInfo.setMsg("未获取到客户端信息");
            client.sendEvent("connect_error",JSONUtil.toJsonStr(messageInfo));
        }
        //存储SocketIOClient，用于发送消息
        socketIOClientMap.put(clientId, client);
        boolean success = redisCache.set(CacheConstants.REDIS_KEY_SOCKET_CLIENT + ":" + clientId, client.getSessionId(),30, TimeUnit.DAYS);
        if(!success){
            log.error("redis存储异常："+clientId);
        }
//        UUID uuid =(UUID) redisUtil.get(MqConstants.REDIS_KEY_SOCKET_CLIENT + ":" + clientId);
//        SocketIOClient s = socketIoServer.getClient(uuid);
        //todo 如果client存在未处理任务，直接拉取，发送到前端
        log.info("客户端:" + client.getSessionId() + "已连接,mac=" + clientId);
        /*try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }*/
    }

    /**
     * 客户端关闭连接时触发
     *
     * @param client
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String clientId = client.getHandshakeData().getSingleUrlParam("clientId");
        SocketIOClient socketIOClient = socketIOClientMap.get(clientId);
        if (null != socketIOClient) {
            socketIOClientMap.remove(socketIOClient);
        }
//        redisUtil.del(MqConstants.REDIS_KEY_SOCKET_CLIENT + ":" + clientId);
        log.info("客户端:" + client.getSessionId() + "断开连接");
    }

    /**
     * 监听客户端事件messageEventInfo
     *
     * @param client  　客户端信息
     * @param request 请求信息
     * @param data    　客户端发送数据
     */
    @OnEvent(value = "messageEventInfo")
    public void messageEventInfo(SocketIOClient client, AckRequest request, MessageInfo data) {
        log.info("发来消息：" + data);
        data.setTargetId(RandomUtil.randomNumbers(7));
        //回发消息
//        client.sendEvent("messageEventTest", "发送给messageEventTest==;" + JSONUtil.toJsonStr(data));
        client.sendEvent("messageEventInfo", "messageEventInfo==;" + JSONUtil.toJsonStr(data));
        //广播消息
        sendBroadcast();
    }

    /**
     * 结束流程,如web结束每份原料的分解任务,则清除一下redis,即结束一个分解,反馈一次
     *
     *
     * @param client
     * @param request
     * @param data
     * @return void
     * @author yw
     * @date 2022-03-19 19:39:34
     */
    @OnEvent(value = "messageEventInfo")
    public void done(SocketIOClient client, AckRequest request, MessageInfo data){
        //todo
    }


    /**
     * 监听客户端事件messageEventTest
     *
     * @param client 　客户端信息
     * @param data   　客户端发送数据
     */
    @OnEvent(value = "messageEventTest")
    public void messageEventTest(SocketIOClient client, MessageInfo data) {
        log.info("发来消息：" + data);
        //回发消息
        data.setTargetId(RandomUtil.randomNumbers(7));
//        client.sendEvent("messageEventInfo", "发送给==messageEventInfo;" + JSONUtil.toJsonStr(data));
//        client.sendEvent("messageEventTest", "发送给==messageEventTest;" + JSONUtil.toJsonStr(data));
//        sendBroadcast();
    }

    @OnEvent(value = "topicDefault")
    public void topicDefault(SocketIOClient client, MessageInfo data) {
        log.info("发来消息：" + data);
        //回发消息
        data.setTargetId(RandomUtil.randomNumbers(7));
//        client.sendEvent("messageEventInfo", "发送给==messageEventInfo;" + JSONUtil.toJsonStr(data));
        client.sendEvent("topicDefault", "发送给==topicDefault;" + JSONUtil.toJsonStr(data));
        sendBroadcast();
    }

    /**
     * 广播消息，暂时值考虑在线的
     */
    public void sendBroadcast() {
        for (SocketIOClient client : socketIOClientMap.values()) {
            if (client.isChannelOpen()) {
                client.sendEvent("broadcast", "当前时间", LocalDateTime.now());
            }
        }
    }
}